using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.IO;
using System.Collections.ObjectModel;
using System.Xml;

namespace CodePlex.Resourceful.Wadl
{

    public class ApplicationResource
    {
        private readonly string _path;
        private readonly ReadOnlyList<TemplateParameter> _templateParameters;
        private readonly List<ApplicationResource> _resources = new List<ApplicationResource>();

        internal ApplicationResource(string path, params TemplateParameter[] templateParameters)
        {
            _path = path;
            _templateParameters = new ReadOnlyList<TemplateParameter>(templateParameters);
        }

        public string Path {
            get {
                return _path;
            }
        }

        public ReadOnlyList<TemplateParameter> TemplateParameters
        {
            get
            {
                return _templateParameters;
            }
        }

        public ReadOnlyList<ApplicationResource,string> Resources
        {
            get
            {
                return new ReadOnlyList<ApplicationResource, string>(_resources, delegate(ApplicationResource item)
                {
                    return item.Path;
                });
            }
        }

        internal ApplicationResource NewResource(string path, params TemplateParameter[] templateParameters)
        {
            return InternalUtils.NewResource(_resources, this.Resources, path, templateParameters);
        }

        internal ApplicationResource FindResource(string relativeUri, NameValuePairs templateParameters)
        {
            return InternalUtils.FindResource(this.Resources, relativeUri, templateParameters);
        }
    }













    public class TemplateParameter
    {
        private readonly string _name;
        private readonly string _type;

        public TemplateParameter(string name, string type)
        {
            _name = name;
            _type = type;
        }

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public string Type
        {
            get
            {
                return _type;
            }
        }
    }

   
   









    public class ApplicationDescription
    {
        private readonly Dictionary<string, ApplicationMethod> _methods = new Dictionary<string, ApplicationMethod>();
        private readonly List<ApplicationResource> _resources = new List<ApplicationResource>();
        private readonly List<Tuple<ApplicationResource, ApplicationMethod>> _bindings = new List<Tuple<ApplicationResource, ApplicationMethod>>();

        protected List<ApplicationResource> ResourceList
        {
            get
            {
                return _resources;
            }
        }

        protected void AddMethod(string id, ApplicationMethod method)
        {
            _methods[id] = method;
        }

        public void Bind(ApplicationResource resource, ApplicationMethod method)
        {
            _bindings.Add(new Tuple<ApplicationResource, ApplicationMethod>(resource, method));
        }

        public ReadOnlyList<ApplicationResource, string> Resources
        {
            get
            {
                return new ReadOnlyList<ApplicationResource, string>(_resources, delegate(ApplicationResource item)
                {
                    return item.Path;
                });
            }
        }

        public ApplicationResource FindResource(string relativeUri, NameValuePairs templateParameters)
        {
            return InternalUtils.FindResource(this.Resources, relativeUri, templateParameters);


        }

        public ApplicationMethod FindMethod(ApplicationResource resource, string methodName)
        {
            Tuple<ApplicationResource, ApplicationMethod> rt = _bindings.Find(delegate(Tuple<ApplicationResource, ApplicationMethod> item)
            {
                return item.Value1 == resource && methodName.Equals(item.Value2.Name);

            });
            return rt == null ? null : rt.Value2;
        }

        public ReadOnlyList<ApplicationMethod, string> FindMethods(ApplicationResource resource)
        {

            return new ReadOnlyList<ApplicationMethod, string>(_bindings.FindAll(delegate(Tuple<ApplicationResource, ApplicationMethod> item)
            {
                return item.Value1 == resource;
            }).ConvertAll<ApplicationMethod>(delegate(Tuple<ApplicationResource, ApplicationMethod> item)
            {
                return item.Value2;
            }), delegate(ApplicationMethod item)
            {
                return item.Name;
            });

        }

        public static void ToXml(Uri baseUri, ApplicationDescription description, XmlTextWriter writer)
        {
            writer.WriteStartDocument();

            using (Utils.StartElement(writer, "application"))
            {
                writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
                writer.WriteAttributeString("xsi:schemaLocation", "http://wadl.dev.java.net/wadl20061109.xsd");
                writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
                writer.WriteAttributeString("xmlns", "http://research.sun.com/wadl/2006/10");

                using (Utils.StartElement(writer, "resources"))
                {
                    writer.WriteAttributeString("base", baseUri.ToString());

                    foreach (ApplicationResource resource in description.Resources)
                    {
                         ToXml(description, writer, resource);
                    }
                }

            }
        }

        private static void ToXml(ApplicationDescription description, XmlTextWriter writer, ApplicationResource resource)
        {
            using (Utils.StartElement(writer, "resource"))
            {
                writer.WriteAttributeString("path", resource.Path);

                if (resource.Path.Contains("{"))
                {
                    using (Utils.StartElement(writer, "param"))
                    {
                        writer.WriteAttributeString("name", resource.TemplateParameters[0].Name);
                        writer.WriteAttributeString("style", "template");
                        writer.WriteAttributeString("type", resource.TemplateParameters[0].Type);
                    }
                }



                foreach (ApplicationMethod method in description.FindMethods(resource))
                {
                    using (Utils.StartElement(writer, "method"))
                    {
                        writer.WriteAttributeString("name", method.Name);
                        writer.WriteAttributeString("id", method.ID);

                        using (Utils.StartElement(writer, "request"))
                        {

                        }

                        using (Utils.StartElement(writer, "response"))
                        {
                            foreach (ApplicationRepresentation representation in method.ResponseRepresentations)
                            {
                                using (Utils.StartElement(writer, "representation"))
                                {
                                    writer.WriteAttributeString("mediaType", representation.MediaType);

                                    //writer.WriteAttributeString("element", "User");
                                }

                            }


                        }
                    }
                }

                foreach (ApplicationResource child in resource.Resources)
                {
                    ToXml(description, writer, child);
                }
            }
        }
    }

















    public class ApplicationRepresentation
    {
        private readonly string _mediaType;

        internal ApplicationRepresentation(string mediaType)
        {
            _mediaType = mediaType;
        }

        public string MediaType
        {
            get
            {
                return _mediaType;
            }
        }
    }


    public class ApplicationMethod
    {

        private readonly Dictionary<string, ApplicationRepresentation> _requestRepresentations;
        private readonly Dictionary<string, ApplicationRepresentation> _responseRepresentations;

        private readonly string _id;
        private readonly string _name;

        public ApplicationMethod(string id, string name, 
            Dictionary<string, ApplicationRepresentation> requestRepresentations,
            Dictionary<string, ApplicationRepresentation> responseRepresentations)
        {
            _id = id;
            _name = name;
            _requestRepresentations = requestRepresentations==null?new Dictionary<string, ApplicationRepresentation>():requestRepresentations;
            _responseRepresentations = responseRepresentations == null ? new Dictionary<string, ApplicationRepresentation>() : responseRepresentations;
        }


        protected void AddRequestRepresentation(string mediaType, ApplicationRepresentation rep)
        {
            _requestRepresentations[mediaType] = rep;
        }

        protected void AddResponseRepresentation(string mediaType, ApplicationRepresentation rep)
        {
            _responseRepresentations[mediaType] = rep;
        }

        public string ID
        {
            get
            {
                return _id;
            }
        }

        virtual public string Name
        {
            get
            {
                return _name;
            }
        }


        public ReadOnlyList<ApplicationRepresentation, string> RequestRepresentations
        {
            get
            {
                return new ReadOnlyList<ApplicationRepresentation, string>(_requestRepresentations.Values, delegate(ApplicationRepresentation item)
                {
                    return item.MediaType;
                });
            }
        }

        public ReadOnlyList<ApplicationRepresentation, string> ResponseRepresentations
        {
            get
            {
                return new ReadOnlyList<ApplicationRepresentation, string>(_responseRepresentations.Values, delegate(ApplicationRepresentation item)
                {
                    return item.MediaType;
                });
            }
        }
    }
































    public class LocalApplicationDescription : ApplicationDescription
    {



        public LocalApplicationMethod NewMethod(string id, string name,Action<IRepresentationContext> handler)
        {
            LocalApplicationMethod method = new LocalApplicationMethod(id, name, handler);
            this.AddMethod(id, method);
            return method;

        }

        public ApplicationResource NewResource(string path, params TemplateParameter[] templateParameters)
        {
            return InternalUtils.NewResource(this.ResourceList, this.Resources, path, templateParameters);
        }




       



    

        


    }


    



    public class LocalApplicationMethod : ApplicationMethod
    {


        private readonly Action<IRepresentationContext> _handler;

        internal LocalApplicationMethod(string id, string name, Action<IRepresentationContext> handler)
            : base(id, name,null,null)
        {
            _handler = handler;
        }



        public Action<IRepresentationContext> Handler
        {
            get
            {
                return _handler;
            }
        }




        public ApplicationRepresentation NewRequestRepresentation(string mediaType)
        {
            ApplicationRepresentation rep = new ApplicationRepresentation(mediaType);
            this.AddRequestRepresentation(mediaType, rep);
            return rep;
        }

        public ApplicationRepresentation NewResponseRepresentation(string mediaType)
        {
            ApplicationRepresentation rep = new ApplicationRepresentation(mediaType);
            this.AddResponseRepresentation(mediaType, rep);
            return rep;
        }
    }











}
